package Helpers;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import Annotations.IsClientCommand;

public class ApplicationInitialiser {
		
	 private static final class Nested
     {
         static final ApplicationInitialiser _instance = new ApplicationInitialiser();
     }
	 
	 private ApplicationInitialiser()
	 {
		 
	 }

     public static ApplicationInitialiser getInstance()
     {
           return Nested._instance;
     }
     
     private boolean isInit;
     public boolean getInitStatus() { return isInit; }
     private void setInitStatus(boolean value) { isInit = value; }
     
     public void Initialize()
     {		
    	 if(getInitStatus())
    	 {
    		 return;
    	 }
    	 
    	 Context ctx = null;
    	 try {
			ctx = new InitialContext();
    	 } catch (NamingException e) {
    		 ApplicationLogger.getInstance().LOG.error(e.getMessage());
    	 }
    	 
    	 ApplicationConfiguration.getInstance().LoadCurrentFacade();
    	 
    	 ApplicationLogger.getInstance().LOG.info("Command List init process Start");
    	 try {
    		 List<String> commandList = new ArrayList<String>();
    		 Class[] commandClassList = getClasses("ClientCommands");
    		
    		for (Class item : commandClassList) {
    			boolean commandIsClient = item.getAnnotation(IsClientCommand.class) != null;
    			if(commandIsClient)
    			{
	    			String simpleName = item.getSimpleName();
	    			ApplicationLogger.getInstance().LOG.info(simpleName + " was added to JNDI");
	    			commandList.add(simpleName);
	    			ctx.bind(simpleName, item.newInstance());
    			}
			}
    		ctx.bind("CommandList", commandList);
    	} catch (NamingException e) {
    		 ApplicationLogger.getInstance().LOG.error(e.getMessage());
 		} catch (ClassNotFoundException e) {
 			ApplicationLogger.getInstance().LOG.error(e.getMessage());
		} catch (IOException e) {
			ApplicationLogger.getInstance().LOG.error(e.getMessage());
		} catch (InstantiationException e) {
			ApplicationLogger.getInstance().LOG.error(e.getMessage());
		} catch (IllegalAccessException e) {
			ApplicationLogger.getInstance().LOG.error(e.getMessage());
		}

    	setInitStatus(true);
    	ApplicationLogger.getInstance().LOG.info("Application Initialised");
    	 
//    	ApplicationLogger.getInstance().LOG.info("Sceleton server init process Start");
//    	IFacade ifa = null;
//    	try {
//    		ApplicationLogger.getInstance().LOG.info("Trying to get facade from JNDI");
//			ifa = (IFacade) ctx.lookup("Facade");
//			ApplicationLogger.getInstance().LOG.info("Facade tried. name " + ifa.getClass().getName());
//		} catch (NamingException e) {
//			ApplicationLogger.getInstance().LOG.error(e.getMessage());
//		} 
//		if(ifa != null)
//		{
//			 ApplicationLogger.getInstance().LOG.info("Facade is not null");
//	 		GeneralFacadeSceleton gfs = new GeneralFacadeSceleton(ifa);
//	 		gfs.start();
//		}
//		else
//		{
//			 ApplicationLogger.getInstance().LOG.error("ERROR: Facade is null");
//		}
     }
     /**
      * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
      *
      * @param packageName The base package
      * @return The classes
      * @throws ClassNotFoundException
      * @throws IOException
      */
     public Class[] getClasses(String packageName)
             throws ClassNotFoundException, IOException {
         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
         assert classLoader != null;
         String path = packageName.replace('.', '/');
         Enumeration<URL> resources = classLoader.getResources(path);
         List<File> dirs = new ArrayList<File>();
         while (resources.hasMoreElements()) {
             URL resource = resources.nextElement();
             dirs.add(new File(resource.getFile()));
         }
         ArrayList<Class> classes = new ArrayList<Class>();
         for (File directory : dirs) {
             classes.addAll(findClasses(directory, packageName));
         }
         return classes.toArray(new Class[classes.size()]);
     }
     /**
      * Recursive method used to find all classes in a given directory and subdirs.
      *
      * @param directory   The base directory
      * @param packageName The package name for classes found inside the base directory
      * @return The classes
      * @throws ClassNotFoundException
      */
     private List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
         List<Class> classes = new ArrayList<Class>();
         if (!directory.exists()) {
             return classes;
         }
         File[] files = directory.listFiles();
         for (File file : files) {
             if (file.isDirectory()) {
                 assert !file.getName().contains(".");
                 classes.addAll(findClasses(file, packageName + "." + file.getName()));
             } else if (file.getName().endsWith(".class")) {
                 classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
             }
         }
         return classes;
     }
}
